package cn.virens.web.components.spring;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.Ordered;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

import cn.hutool.core.util.StrUtil;
import cn.virens.exception.ExceptionUtil;

/**
 * 以.json为后缀的请求，异常将以json形式返回
 * 
 * @author loioi
 */
public class UniversallyExceptionResolver implements HandlerExceptionResolver, Ordered {
	private static final Logger logger = LoggerFactory.getLogger(UniversallyExceptionResolver.class);

	private Map<Class<? extends Exception>, String> exceptionMessages = new HashMap<>();
	private Map<Class<? extends Exception>, String> exceptionMappings = new HashMap<>();

	private List<Class<? extends Exception>> exceptionExcluded = new ArrayList<>();

	private String defaultErrorView = null;
	private boolean showException = true;
	private int order = Ordered.LOWEST_PRECEDENCE;

	@Override
	public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
		if (logger.isErrorEnabled() && showException) {
			logger.error(ex.getMessage(), ex);
		} else if (logger.isDebugEnabled()) {
			logger.error(ex.getMessage());
		}

		// 根据不同类型的异常进行处理
		if (excludedException(ex)) {
			return null;
		} else if (isJsonReq(request)) {
			return getModelAndView1(ex, request);
		} else {
			return getModelAndView0(ex, request);
		}
	}

	@Override
	public int getOrder() {
		return order;
	}

	public void setOrder(int order) {
		this.order = order;
	}

	/**
	 * 是否显示异常 详细
	 * 
	 * @return
	 */
	public boolean isShowException() {
		return showException;
	}

	/**
	 * 设置 是否显示异常
	 * 
	 * @param showException 显示异常
	 */
	public void setShowException(boolean showException) {
		this.showException = showException;
	}

	/**
	 * 设置 默认异常视图
	 * 
	 * @param defaultErrorView 默认异常视图
	 */
	public void setDefaultErrorView(String defaultErrorView) {
		this.defaultErrorView = defaultErrorView;
	}

	/**
	 * 添加 异常消息
	 * 
	 * @param exception 异常类型
	 * @param message   异常消息
	 */
	public void addExceptionMessage(Class<? extends Exception> exception, String message) {
		this.exceptionMessages.put(exception, message);
	}

	/**
	 * 添加 异常视图
	 * 
	 * @param exception 异常类型
	 * @param view      异常视图
	 */
	public void addExceptionMapping(Class<? extends Exception> exception, String view) {
		this.exceptionMappings.put(exception, view);
	}

	/**
	 * 添加 被排除的异常类
	 * 
	 * @param exceptionExcluded 被排除的异常类
	 */
	public void addExceptionExcluded(Class<? extends Exception> exceptionExcluded) {
		this.exceptionExcluded.add(exceptionExcluded);
	}

	/**
	 * 判断 当前异常是否是被排除
	 * 
	 * @param  ex 异常
	 * @return    是否被排除
	 */
	protected boolean excludedException(Exception ex) {
		for (Class<?> clazz : exceptionExcluded) {
			if (isAssignableException(clazz, ex)) {//
				return true;
			}
		}

		return false;
	}

	/**
	 * 获取 异常页面视图
	 * 
	 * @param  ex  异常对象
	 * @param  req 请求对象
	 * @return     页面视图
	 */
	protected ModelAndView getModelAndView0(Exception ex, HttpServletRequest req) {
		// 查找异常指定的ViewName
		ModelAndView modelAndView = new ModelAndView();
		for (Class<?> calzz : exceptionMappings.keySet()) {
			if (isAssignableException(calzz, ex)) {
				modelAndView.setViewName(exceptionMappings.get(calzz));
				modelAndView.addObject("httpUrl", httpUrl(req));
				modelAndView.addObject("method", method(req));
				modelAndView.addObject("ex", ex);

				return modelAndView;
			}
		}

		// 添加默认异常类
		modelAndView.setViewName(defaultErrorView);
		modelAndView.addObject("httpUrl", httpUrl(req));
		modelAndView.addObject("method", method(req));
		modelAndView.addObject("ex", ex);

		return modelAndView;
	}

	/**
	 * 获取 异常消息视图
	 * 
	 * @param  ex  异常对象
	 * @param  req 请求对象
	 * @return     消息视图
	 */
	protected ModelAndView getModelAndView1(Exception ex, HttpServletRequest req) {
		ModelAndView modelAndView = new ModelAndView();
		for (Class<?> calzz : exceptionMessages.keySet()) {
			if (isAssignableException(calzz, ex)) {
				modelAndView.addObject("message", exceptionMessages.get(calzz));
				modelAndView.addObject("code", ExceptionUtil.getCode(ex));
				modelAndView.addObject("success", false);

				return modelAndView;
			}
		}

		// 添加默认异常类
		modelAndView.addObject("message", ExceptionUtil.getMessage(ex));
		modelAndView.addObject("code", ExceptionUtil.getCode(ex));
		modelAndView.addObject("success", false);

		return modelAndView;
	}

	private static String httpUrl(HttpServletRequest req) {
		return req.getRequestURI();
	}

	private static String method(HttpServletRequest req) {
		return req.getMethod();
	}

	private static boolean isJsonReq(HttpServletRequest request) {
		return StrUtil.endWith(request.getRequestURI(), ".json");
	}

	private static boolean isAssignableException(Class<?> c, Exception ex) {
		return c.isAssignableFrom(ex.getClass());
	}
}
